﻿using AntMovement.Comm.Helper;
using AntMovement.Comm.Model;
using AntMovement.Core.LabelService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AntMovement.Comm
{
    public class Write : IChainedHandle
    {
        private Func<int, OutTimeModel> _query;
        public Func<int, OutTimeModel> Query => _query;
        private Func<int, bool, bool> _updateTime;
        public Func<int, bool, bool> UpdateTime => _updateTime;
        private ITagManager _tagManager;

        public Write(ITagManager tagManager, Func<int, OutTimeModel> query, Func<int, bool, bool> updateTime)
        {
            _tagManager = tagManager;
            _query = query;
            _updateTime = updateTime;
        }

        public void Execute(byte[] request, List<byte> respond)
        {
            respond.Add(request[2]);
            respond.Add(request[3]);
            respond.Add(request[4]);
            respond.Add(request[5]);
            int id = BytesHelper.BytesToInt(request[2], request[3], request[4], request[5]);
            OutTimeModel outTime = _query.Invoke(id);
            if (outTime != null)
            {
                string[] labels = new string[request[11]];
                List<object> values = new List<object>();
                int labelLength = 13;
                try
                {
                    for (int i = 0; i < labels.Length; i++)
                    {
                        labels[i] = Encoding.ASCII.GetString(request.Skip(labelLength).Take(request[labelLength - 1]).ToArray());
                        labelLength += request[labelLength - 1];
                    }
                    Console.WriteLine(string.Join(',', labels));
                }
                catch (Exception)
                {
                    respond.AddRange(BytesHelper.ToParseTagException(request[10]));
                    return;
                }
                if (labels.Length > 1 && request[labelLength - 1] == 0xA0)
                {
                    respond.Add(3);
                    respond.Add(0);
                    respond.Add(9);
                    respond.Add(0);
                    respond.Add(respond[10]);
                    return;
                }
                try
                {
                    for (int i = 0; i < labels.Length; i++)
                    {
                        values.Add(ToValue(request[labelLength], request.Skip(labelLength + 1).ToArray(), out int count));
                        labelLength += count;
                    }
                    Console.WriteLine(string.Join(',', values));
                }
                catch (Exception)
                {
                    respond.AddRange(BytesHelper.ToAnalyticValueAnomaly(request[10]));
                    return;
                }
                try
                {
                    if (labels.Length == 1)
                    {
                        _tagManager.SetValue(labels[0], values[0]);
                    }
                    else
                    {
                        _tagManager.SetValues(labels, values.ToArray());
                    }
                    respond.Add(3);
                    respond.Add(0);
                    respond.Add(0);
                    respond.Add(0);
                    respond.Add(request[10]);
                }
                catch (NotImplementedException)
                {
                    respond.AddRange(BytesHelper.ToNoLabel(request[10]));
                }
                catch (ArgumentException ex)
                {
                    respond.Add(3);
                    respond.Add(0);
                    if (ex.Message.Contains("The label array length is not equal to the value array length"))
                    {
                        respond.Add(6);
                    }
                    else if (ex.Message.Contains("Value setting failure"))
                    {
                        respond.Add(7);
                    }
                    else if (ex.Message.Contains("This type is not implemented"))
                    {
                        respond.Add(10);
                    }
                    respond.Add(0);
                    respond.Add(respond[10]);
                }
            }
            else
            {
                respond.AddRange(BytesHelper.ToUnregistered(request[10]));
            }
        }

        private object ToValue(byte datatype, byte[] bytes, out int count)
        {
            count = 0;
            switch (datatype)
            {
                case 0xC4: //"Int32"
                    count = 4;
                    return StructToBytes.GetValue(typeof(Int32), bytes, 0);
                case 0xC8: //"UInt32"
                    count = 4;
                    return StructToBytes.GetValue(typeof(UInt32), bytes, 0);
                case 0xCA://"Single"
                    count = 4;
                    return StructToBytes.GetValue(typeof(float), bytes, 0);
                case 0xC5://"Int64" 
                    count = 8;
                    return StructToBytes.GetValue(typeof(Int64), bytes, 0);
                case 0xC9: //"UInt64"
                    count = 8;
                    return StructToBytes.GetValue(typeof(UInt64), bytes, 0);
                case 0xCB://"Double"
                    count = 8;
                    return StructToBytes.GetValue(typeof(Double), bytes, 0);
                case 0xC3: //"Int16"
                    count = 2;
                    return StructToBytes.GetValue(typeof(Int16), bytes, 0);
                case 0xC7: //"UInt16"
                    count = 2;
                    return StructToBytes.GetValue(typeof(UInt16), bytes, 0);
                case 0xD1://"Byte"
                    count = 1;
                    return StructToBytes.GetValue(typeof(Byte), bytes, 0);
                case 0xC1://"Boolean"
                    count = 1;
                    return StructToBytes.GetValue(typeof(Boolean), bytes, 0);
                case 0xD0: //"String"
                    int length = bytes[0] + (bytes[1] << 8);
                    count = length + 2;
                    return Encoding.ASCII.GetString(bytes.Skip(2).Take(length).ToArray());
            }
            throw new ArgumentException("This type is not implemented...");
        }
    }
}
